CurrentStatusTab.onCheckboxChanged   F
last analyzed

Complexity

Conditions 19

Size

Total Lines 9
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 19
eloc 8
dl 0
loc 9
rs 0.5999
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like CurrentStatusTab.onCheckboxChanged often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import React from 'react';
2
import PropTypes from 'prop-types';
3
import {
4
  ButtonToolbar,
5
  ButtonGroup,
6
  Button,
7
  Row,
8
  Col,
9
  Pagination,
10
  Label,
11
} from 'react-bootstrap';
12
import moment from 'moment';
13
import BaseRefreshableTab from './BaseRefreshableTab';
14
import Loading from '../Loading';
15
import Table from '../Table';
16
import Modal from '../modal/Modal';
17
import StatusModal from '../modal/StatusModal';
18
import dateUtil from '../../common/date-util';
19
20
import Api from '../../common/api';
21
22
const ASTERISK = '✱';
23
const DATE_FORMAT = 'YYYY-MM-DD HH:mm';
24
25
export default class CurrentStatusTab extends BaseRefreshableTab {
26
  constructor(props) {
27
    super(props);
28
29
    this.state = {
30
      items: [],
31
      checkedItems: [],
32
      page: 1,
33
      totalCount: 0,
34
      countPerPage: 10,
35
      buttonDisabled: {
36
        add: false,
37
        modify: true,
38
        remove: true,
39
        clone: true,
40
        activate: true,
41
      },
42
      error: null,
43
      modal: {
44
        visible: false,
45
        mode: 'confirm',
46
        title: '',
47
        onSuccess: () => {},
48
        onConfirm: () => {},
49
        onClose: () => this.setSubState('modal', { visible: false }),
50
        content: '',
51
        data: null,
52
      },
53
      showLoading: false,
54
    };
55
56
    this.options = {
57
      statusTypes: props.statusTypes ? props.statusTypes : [],
58
      deviceTypes: props.deviceTypes ? props.deviceTypes : [],
59
    };
60
61
    this.columns = [
62
      {
63
        title: 'ID',
64
        isChildRow: true,
65
        display: row => <span>{row.id}</span>,
66
      },
67
      {
68
        title: '상태 타입',
69
        display: (row) => {
70
          if (!row.type) { return ''; }
71
          let typeLabel = 'Unknown';
72
          for (let i = 0; i < this.options.statusTypes.length; i++) {
73
            if (row.type === this.options.statusTypes[i].value) {
74
              typeLabel = this.options.statusTypes[i].label;
75
              break;
76
            }
77
          }
78
          return <span>{typeLabel}</span>;
79
        },
80
      },
81
      { title: '제목', display: row => <span>{row.title}</span> },
82
      {
83
        title: '기간',
84
        display: (row) => {
85
          if (!row.startTime || !row.endTime) {
86
            return <span>{ASTERISK}</span>;
87
          }
88
          const start = moment(row.startTime);
89
          const end = moment(row.endTime);
90
          const duration = start.from(end, true);
91
          return <span>{`${dateUtil.formatDate(start, DATE_FORMAT)} ~ ${dateUtil.formatDate(end, DATE_FORMAT)} (${duration})`}</span>;
92
        },
93
      },
94
      {
95
        title: '디바이스 타입',
96
        display: (row) => {
97
          if (!row.deviceTypes) {
98
            return <span />;
99
          }
100
          if (row.deviceTypes.length === this.options.deviceTypes.length) {
101
            return <span>{ASTERISK}</span>;
102
          }
103
          return row.deviceTypes
104
            ? (
105
              <span>
106
                {row.deviceTypes.map((device) => {
107
                  let typeLabel = 'Unknown';
108
                  for (let i = 0; i < this.options.deviceTypes.length; i++) {
109
                    if (device === this.options.deviceTypes[i].value) {
110
                      typeLabel = this.options.deviceTypes[i].label;
111
                      break;
112
                    }
113
                  }
114
                  return typeLabel;
115
                }).join(', ')}
116
              </span>
117
            )
118
            : <span />;
119
        },
120
      },
121
      {
122
        title: '디바이스 버전',
123
        display: row => ((row.deviceSemVersion === '*') ? <span>{ASTERISK}</span> : row.deviceSemVersion),
124
      },
125
      {
126
        title: '앱 버전',
127
        display: row => ((row.appSemVersion === '*') ? <span>{ASTERISK}</span> : row.appSemVersion),
128
      },
129
      {
130
        title: '내용',
131
        isChildRow: true,
132
        display: row => (!row.contents ? '' : <span dangerouslySetInnerHTML={{ __html: row.contents }} />),
133
      },
134
      {
135
        title: '활성화',
136
        display: row => <Label bsStyle={row.isActivated ? 'success' : 'default'}>{row.isActivated ? 'ON' : 'OFF'}</Label>,
137
      },
138
    ];
139
  }
140
141
  onCheckboxChanged(checkedItems) {
142
    let buttonDisabled = { add: false, modify: true, remove: true, clone: true, activate: true };
143
    if (checkedItems.length === 1) {
144
      buttonDisabled = { add: false, modify: false, remove: false, clone: false, activate: false };
145
    } else if (checkedItems.length > 1) {
146
      buttonDisabled = { add: false, modify: true, remove: false, clone: true, activate: false };
147
    }
148
    this.setState({ buttonDisabled, checkedItems });
149
  }
150
151
  remove(items) {
152
    if (items instanceof Array && items.length > 0) {
153
      Promise.all(items.map(item => Api.removeStatus(item.id)))
154
        .then(() => {
155
          this.refresh();
156
          this.state.modal.onClose();
157
        })
158
        .catch(error => this.setState({ error }));
159
    }
160
  }
161
162
  setActivation(isActivated, items) {
163
    if (items instanceof Array && items.length > 0) {
164
      const api = isActivated ? Api.activateStatus : Api.deactivateStatus;
165
      Promise.all(items.map(item => api(item.id)))
166
        .then(() => {
167
          this.refresh();
168
          this.state.modal.onClose();
169
        })
170
        .catch(error => this.setState({ error }));
171
    }
172
  }
173
174
  refresh() {
175
    const {
176
      page,
177
      countPerPage,
178
    } = this.state;
179
180
    this.setState({ showLoading: true });
181
    const offset = (page - 1) * countPerPage;
182
    const limit = countPerPage;
183
    return Api.getStatus('current', (page - 1) * offset, limit)
184
      .then((response) => {
185
        this.setState({
186
          items: response.data.data,
187
          totalCount: response.data.totalCount,
188
          error: null,
189
          checkedItems: [],
190
          showLoading: false,
191
        });
192
        this.table.setChecked(false);
193
      })
194
      .catch((error) => {
195
        this.setState({
196
          items: [],
197
          totalCount: 0,
198
          error,
199
          checkedItems: [],
200
          showLoading: false,
201
        });
202
        this.table.setChecked(false);
203
      });
204
  }
205
206
  onClickButton(modalName) {
207
    const { checkedItems } = this.state;
208
    switch (modalName) {
209
      case 'add':
210
        this.setSubState('modal', {
211
          visible: true,
212
          mode: 'add',
213
          onSuccess: () => this.refresh(),
214
          data: null,
215
        });
216
        break;
217
      case 'clone':
218
        this.setSubState('modal', {
219
          visible: true,
220
          mode: 'add',
221
          onSuccess: () => this.refresh(),
222
          data: checkedItems[0],
223
        });
224
        break;
225
      case 'modify':
226
        this.setSubState('modal', {
227
          visible: true,
228
          mode: 'modify',
229
          onSuccess: () => this.refresh(),
230
          data: checkedItems[0],
231
        });
232
        break;
233
      case 'remove':
234
        this.setSubState('modal', {
235
          visible: true,
236
          mode: 'confirm',
237
          title: '삭제 확인',
238
          onConfirm: () => this.remove(checkedItems),
239
          content: <p>{checkedItems.length} 건의 데이터를 삭제하시겠습니까?</p>,
240
        });
241
        break;
242
      case 'activate':
243
        this.setSubState('modal', {
244
          visible: true,
245
          mode: 'confirm',
246
          title: '활성화 확인',
247
          onConfirm: () => this.setActivation(true, checkedItems),
248
          content: <p>{checkedItems.length} 건의 데이터를 활성화 하시겠습니까?</p>,
249
        });
250
        break;
251
      case 'deactivate':
252
        this.setSubState('modal', {
253
          visible: true,
254
          mode: 'confirm',
255
          title: '비활성화 확인',
256
          onConfirm: () => this.setActivation(false, checkedItems),
257
          content: <p>{checkedItems.length} 건의 데이터를 비활성화 하시겠습니까?</p>,
258
        });
259
        break;
260
      default:
261
        break;
262
    }
263
  }
264
265
  renderModal() {
266
    const {
267
      modal,
268
    } = this.state;
269
270
    if (modal.mode === 'add' || modal.mode === 'modify') {
271
      return (
272
        <StatusModal
273
          visible={modal.visible}
274
          mode={modal.mode}
275
          options={this.options}
276
          data={modal.data}
277
          onSuccess={() => this.refresh()}
278
          onClose={() => modal.onClose()}
279
        />
280
      );
281
    }
282
    return (
283
      <Modal
284
        visible={modal.visible}
285
        mode={modal.mode}
286
        title={modal.title}
287
        onSuccess={() => modal.onSuccess()}
288
        onConfirm={() => modal.onConfirm()}
289
        onClose={() => modal.onClose()}
290
      >
291
        {modal.content}
292
      </Modal>
293
    );
294
  }
295
296
  render() {
297
    const {
298
      items,
299
      buttonDisabled,
300
      totalCount,
301
      page,
302
      countPerPage,
303
      error,
304
    } = this.state;
305
306
    return (
307
      <div>
308
        <ButtonToolbar>
309
          <ButtonGroup>
310
            <Button onClick={() => this.onClickButton('add')} bsSize="small" disabled={buttonDisabled.add}>등록</Button>
311
            <Button onClick={() => this.onClickButton('clone')} bsSize="small" disabled={buttonDisabled.clone}>복제</Button>
312
          </ButtonGroup>
313
          <Button onClick={() => this.onClickButton('modify')} bsSize="small" disabled={buttonDisabled.modify}>수정</Button>
314
          <ButtonGroup>
315
            <Button onClick={() => this.onClickButton('activate')} bsSize="small" disabled={buttonDisabled.activate}>활성화</Button>
316
            <Button onClick={() => this.onClickButton('deactivate')} bsSize="small" disabled={buttonDisabled.activate}>비활성화</Button>
317
          </ButtonGroup>
318
          <Button onClick={() => this.onClickButton('remove')} bsSize="small" disabled={buttonDisabled.remove}>삭제</Button>
319
        </ButtonToolbar>
320
321
        <Row className="table-info">
322
          <Col xs={6} className="table-info-left">총 {totalCount || 0}건의 데이터</Col>
323
          <Col xs={6} className="table-info-right">{page} / {Math.ceil(totalCount / countPerPage) || 1} 페이지</Col>
324
        </Row>
325
326
        <Table
327
          ref={(t) => { this.table = t; }}
328
          items={items}
329
          columns={this.columns}
330
          error={error}
331
          onCheckboxChange={(newCheckedItems => this.onCheckboxChanged(newCheckedItems))}
332
          onPageChange={newPage => this.setState({ page: newPage }, () => this.refresh())}
333
          page={page}
334
          totalPage={Math.ceil(totalCount / countPerPage) || 1}
335
          showCheckbox
336
        />
337
        <div className="text-center">
338
          <Pagination
339
            prev
340
            next
341
            first
342
            last
343
            items={Math.ceil(totalCount / countPerPage) || 1}
344
            activePage={page || 1}
345
            onSelect={newPage => this.setState({ page: newPage }, () => this.refresh())}
346
          />
347
        </div>
348
        {this.renderModal()}
349
        <Loading show={this.state.showLoading} />
350
      </div>
351
    );
352
  }
353
}
354
355
CurrentStatusTab.propTypes = {
356
  statusTypes: PropTypes.arrayOf(PropTypes.shape({
357
    label: PropTypes.string,
358
    value: PropTypes.string,
359
  })).isRequired,
360
  deviceTypes: PropTypes.arrayOf(PropTypes.shape({
361
    label: PropTypes.string,
362
    value: PropTypes.string,
363
  })).isRequired,
364
};
365